﻿/*

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
  the License. You may obtain a copy of the License at 
  
           http://www.mozilla.org/MPL/ 
  
  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License. 
  
  The Original Code is AST'r Framework.
  
  The Initial Developer of the Original Code is
  ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
  Portions created by the Initial Developer are Copyright (C) 2004-2011
  the Initial Developer. All Rights Reserved.
  
  Contributor(s) :
  
*/
package evideo.display 
{
    import evideo.logging.logger;

    import graphics.display.Background;

    import lunas.containers.BoxContainer;

    import system.data.Data;
    import system.data.ValueObject;
    import system.data.maps.HashMap;
    import system.ioc.ObjectFactory;
    import system.process.Runnable;

    import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
    import flash.events.Event;
    
    /**
     * The core class of all list based ValueObject.
     */
    public class ListValueObject extends Background implements Runnable
    {
        /**
         * Creates a new CoreList instance.
         * @param isFull Indicates if the display is full size or not.
         */
        public function ListValueObject( isFull:Boolean = false )
        {
            super( isFull );
            _map = new HashMap() ;
        }
        
        /**
         * The id of the button in the factory to use to create a new cell in the list.
         */
        public var cellID:String ;
        
        /**
         * The container of this display.
         */
        public function get container():DisplayObjectContainer
        {
            return _container ; 
        }
        
        /**
         * The container of this display.
         */
        public function set container( display:DisplayObjectContainer ):void
        {
            clear() ;
            _container = display ;
        }
        
        /**
         * The IoC factory of the application.
         */
        public var factory:ObjectFactory ;
        
        /**
         * The library path.
         */
        public var libraryPath:String = "" ;
        
        /**
         * Determinates the verbose mode of this display.
         */
        public var verbose:Boolean ;
        
        /**
         * Inserts a new ValueObject into the list.
         */
        public function addVO( vo:ValueObject ):void
        {
            if ( vo == null )
            {
                logger.warn(this + " addVO failed, the passed-in IValueObject not must be 'null' or 'undefined'.") ;
            }
            else if ( containsVO( vo ) )
            {
                logger.warn( this + " addVO( " + vo + ") failed, the IValueObject is already registered in the list." ) ;
            }
            else if ( _container != null )
            {         
                if ( verbose )
                { 
                    logger.info( this + " addVO( " + vo + ") and create a cell with the id : " + cellID ) ;
                }
                var cell:DisplayObject = createNewCell() ;
                if ( cell != null )
                {
                    _map.put( vo , cell ) ;
                    setCell( cell  , vo ) ;
                    _container.addChild( cell ) ;
                }
                else
                {
                    logger.warn(this + " addVO failed, the cell must be a IData and DisplayObject object.") ;
                }
            }
            else
            {
                logger.warn(this + " addVO failed, the container not must be 'null' or 'undefined'.") ;
            }
        }
        
        /**
         * Clear the display.
         */
        public function clear():void
        {
            if ( _container != null && _container is BoxContainer )
            {
                (container as BoxContainer).clear() ;
            }
            _map.clear() ;
        }
        
        /**
         * Returns <code class="prettyprint">true</code> if the list contains a ValueObject with the specified id key in argument.
         * @return <code class="prettyprint">true</code> if the list contains a ValueObject with the specified id key in argument.
         */
        public function containsByID( id:* ):Boolean
        {
            var v:ValueObject ;
            var a:Array = _map.getKeys() ;
            var l:int   = a.length ;
            while(--l > -1)
            {
                v = a[l] as ValueObject ;
                if ( v.id == id ) 
                {
                    return true ;
                }
            }
            return false ;
        }
        
        /**
         * Indicates if the list contains the specified ValueObject.
         */
        public function containsVO( vo:ValueObject ):Boolean
        {
            return _map.containsKey(vo) ;
        }
        
        /**
         * Returns the cell reference of the specified ValueObject.
         * @return the cell reference of the specified ValueObject.
         */
        public function getCell( vo:ValueObject ):*
        {
            return _map.get( vo ) ;
        }
        
        /**
         * Removes an ValueObject into the container.
         */
        public function removeVO( vo:ValueObject ):void
        {
            if ( _container != null )
            {   
                if ( _map.containsKey( vo ) )
                {
                    _container.removeChild( _map.get( vo ) as DisplayObject ) ;
                    _map.remove( vo ) ;
                }
                else
                {
                    logger.warn( this + " removeVO failed. The vo is not in the map." ) ;
                }
            }
            else
            {
                logger.warn(this + " removeVO failed, the container is 'null' or 'undefined'.") ;
            }
        }  
        
        /**
         * Run the process of the specified list.
         * Overrides this method to customize the cell features.
         */
        public function run(...arguments:Array):void
        {
            var vo:ValueObject = arguments[0] as ValueObject ;
            logger.info( this + " select : " + vo ) ;
        }
        
        /**
         * Select the specified ValueObject register in the container.
         */
        public function select( vo:ValueObject ):void
        {
            if ( _map.containsKey( vo ) )
            {
                run(vo) ;
            }
            else
            {
                logger.warn ( this + " select failed, the specified profile don't exist in this container or is 'null' or 'undefined' : " + vo ) ; 
            }
        }
        
        /**
         * Returns the number of elements in the list.
         * @return the number of elements in the list.
         */
        public function size():uint
        {
            return _map.size() ;
        }
        
        /**
         * Updates a ValueObject into the container.
         */
        public function updateVO( vo:ValueObject ):void
        {
            if ( vo == null )
            {
               logger.warn(this + " updateVO failed, the passed-in ValueObject not must be 'null' or 'undefined'.") ;
               return ; 
            }
            var find:ValueObject ;
            var tmp:ValueObject ;
            var a:Array = _map.getKeys() ;
            var l:int = a.length ;
            while(--l > -1)
            {
                tmp = a[l] as ValueObject ;
                if ( tmp.id == vo.id ) 
                {
                    find = tmp ;
                    break ;
                }
            }
            if ( find != null )
            {
                var cell:DisplayObject = getCell( find ) as DisplayObject ;
                if ( cell != null )
                {
                    updateCell( cell , vo ) ;
                    _map.remove( find ) ;
                    _map.put( vo , cell ) ;
                }
                else
                {
                    logger.warn(this + " updateVO failed, no cell find in the list with the finding value object : " + find ) ;
                }
            }
            else
            {
                logger.warn(this + " updateVO failed, a ValueObject with the same id don't exit in the list :" + vo ) ;
            }
        } 
        
        /**
         * @private 
         */
        protected var _map:HashMap ;
        
        /**
         * Creates a new Cell in this container. 
         * This cell must be a DisplayObject reference.
         */
        protected function createNewCell():DisplayObject
        {
            if ( factory != null && cellID != null && factory.containsObjectDefinition(cellID) )
            {
                return ( factory.getObject( cellID ) ) as DisplayObject ;
            }
            else
            {
                return null ;
            }
        }
        
        /**
         * Sets a new Cell in this container. 
         * Overrides this method to customize the cell features.
         */
        protected function setCell( cell:DisplayObject , vo:ValueObject ):DisplayObject
        {
            (cell as Data).data = vo ;
            return cell ;
        }
        
        /**
         * Update a cell in this list. 
         * Overrides this method to customize the cell features.
         */
        protected function updateCell( cell:DisplayObject , vo:ValueObject ):DisplayObject
        {
            if ( cell is Data )
            {
                (cell as Data).data = vo ;
            }
            else
            {
                logger.warn( this + " updateCell failed, the cell must be IData." ) ;
            }
            return cell ;
        }
        
        /**
         * @private
         */
        protected var _container:DisplayObjectContainer ;
        
        /**
         * @private
         */
        protected function selectCell( e:Event ):void
        {
            select( (e.currentTarget as Data).data as ValueObject ) ;
        }
    }
}
